/**
 * @file        gtd.cpp
 * @brief       Defines specific methods related with todo items.
 *
 * @author      Emre Turkay <emreturkay at gmail>
 * @date        May.31.2008
 */

#include "line_parser.hpp"
#include "gtd.hpp"

#include <ctime>

namespace {
    size_t max_index_ = 0;

    size_t get_uuid() { return ++max_index_; }

} // unnamed namespace

namespace gtd
{

gtd::gtd()
{
}

void gtd::save_done(std::ostream& os)
{
    for (std::set<done_item>::const_iterator i = done_items_.begin();
         i != done_items_.end(); ++i)
    {
        i->write(os);
    }
}

project& gtd::get_project(project_id index)
{
    return projects_[index];
}

const project& gtd::get_project(project_id index) const
{
    projects::const_iterator i = projects_.find(index);
    return i->second;
}

void gtd::done(project_id index)
{
    if (!projects_.empty())
    {
        std::ostringstream os;

        if (projects_[index].name())
            os << '+' << *(projects_[index].name()) << ' ';

        const action& a = projects_[index].item(projects_[index].top());

        for (std::set<std::string>::const_iterator i = a.contexts().begin();
             i != a.contexts().end(); ++i)
        {
            os << '@' << *i << ' ';
        }

        os << a.description();

        done_items_.insert(os.str());
        projects_[index].erase(projects_[index].top());
        if (projects_[index].empty())
        {
            projects_.erase(projects_.find(index));
            list_.erase(std::find(list_.begin(), list_.end(), index));
        }
    }
}

void gtd::cancel(project_id index)
{
    if (!projects_.empty())
    {
        projects_[index].erase(projects_[index].top());
        if (projects_[index].empty())
        {
            projects_.erase(projects_.find(index));
            list_.erase(std::find(list_.begin(), list_.end(), index));
        }
    }
}

void gtd::defer(project_id index)
{
    if (!projects_.empty())
    {
        list_.push_back(index);
        list_.erase(std::find(list_.begin(), list_.end(), index));
    }
}

void gtd::append(const std::string& value)
{
    boost::optional<line_parser::result_type> result =
        line_parser::parse(value);

    if (result)
    {
        boost::optional<project_id> p;

        if (result->project)
            p = find_project(*result->project);
        
        if (!p)
            p = create_project(result->project);

        projects_[*p].append(action(result->description, result->contexts));
    }
}

void gtd::prepend(const std::string& value)
{
    boost::optional<line_parser::result_type> result =
        line_parser::parse(value);

    if (result)
    {
        boost::optional<project_id> p;

        if (result->project)
            p = find_project(*result->project);
        
        if (!p)
            p = create_project(result->project);

        projects_[*p].prepend(action(result->description, result->contexts));
    }
}

void gtd::move_back(project_id index)
{
    if (projects_.size() > 1)
    {
        list_.push_back(index);
        list_.erase(std::find(list_.begin(), list_.end(), index));
    }
}

bool gtd::empty() const
{
    return projects_.empty();
}

std::list<gtd::project_id> gtd::project_ids() const
{
    return list_;
}

std::list<gtd::project_id> gtd::project_ids(const std::string& ctx) const
{
    std::list<project_id> result;

    for (std::list<project_id>::const_iterator i = list_.begin();
         i != list_.end(); ++i)
    {
        const project& p = projects_.find(*i)->second;
        const action& a = p.item(p.top());
        if (a.contexts().find(ctx) != a.contexts().end())
            result.push_back(*i);
    }

    return result;
}

const boost::optional<std::string>& gtd::project_name(project_id id) const
{
    return projects_.find(id)->second.name();
}

std::list<project::action_id> gtd::items(project_id id) const
{
    return projects_.find(id)->second.items();
}

const action& gtd::item(project_id id, project::action_id index) const
{
    return projects_.find(id)->second.item(index);
}

size_t gtd::item_count(project_id id) const
{
    return projects_.find(id)->second.item_count();
}

boost::optional<gtd::project_id>
gtd::find_project(const std::string& name) const
{
    for (projects::const_iterator i = projects_.begin();
         i != projects_.end(); ++i)
    {
        if (i->second.name() && (*(i->second.name()) == name))
            return i->first;
    }

    return boost::none;
}

void gtd::load_done(std::istream& is)
{
    while (!is.eof())
    {
        done_item i;
        i.read(is);
        if (i.line().size())
            done_items_.insert(i);
    }
}

const std::set<done_item>& gtd::done_items() const
{
    return done_items_;
}

std::set<done_item> gtd::done_items(boost::gregorian::date_duration d) const
{
    std::set<done_item> result;
    boost::gregorian::date start =
        boost::gregorian::day_clock::local_day() - d + boost::gregorian::days(1);
    boost::gregorian::date_duration dur = d + boost::gregorian::days(1);
    boost::gregorian::date_period p(start, dur);

    for (std::set<done_item>::const_reverse_iterator i = done_items_.rbegin();
         i != done_items_.rend(); ++i)
    {
        if (p.contains(i->time().date()))
            result.insert(*i);
        else
            break;
    }
    return result;
}

gtd::project_id gtd::create_project(boost::optional<std::string> name)
{
    size_t uuid = get_uuid();
    if (name)
        projects_[uuid] = project(*name);
    else
        projects_[uuid] = project();

    list_.push_back(uuid);

    return uuid;
}

} // namespace gtd
